"""
=============================================================
【数据模拟】
=============================================================
"""
import datetime
import random

import pika
from django.core.cache import cache

from pms import models
from yl_pms.utils import PTN


def parse_config():
    """最终返回列表，列表元素为字典
    列表元素字典结构为：
        conf = {
            'projectId': '27',
            'typeId': '21',
            'serialNumbers': [1, 3, 5],
            'quotaAmount': 5,
            'minValue': 1,
            'maxValue': 10,
            'interval': 100
            'decimal': 100
            }
    """
    res = []
    # 每个启用类型对应的模拟数据信息 {type_id: FakeVal}
    dict_type = {}
    # 每个类型对应的指标个数
    dict_tc = {}
    # 每个项目对应的，已启用类型的，已启用的模拟测点 {pid: {type_id: [sc]}}
    dict_pts = {}
    tgt_list = models.FakeTgt.objects.values()
    val_list = models.FakeVal.objects.values()
    if len(tgt_list) != 0 and len(val_list) != 0:
        for val in val_list:
            type_id = val['type_id']
            dict_type[type_id] = val
            quotas = models.SettingsDataQuota.objects.filter(target_type_id=type_id)
            dict_tc[type_id] = len(quotas)
        for tgt in tgt_list:
            type_id = tgt['type_id']
            if dict_type[type_id]['is_enable'] and tgt['is_enable']:
                pid = tgt['pid']
                sc = tgt['sc']
                if pid in dict_pts:
                    td = dict_pts[pid]
                    if type_id in td:
                        td[type_id].append(sc)
                    else:
                        td[type_id] = [sc]
                else:
                    dict_pts[pid] = {type_id: [sc]}
        for p in dict_pts:
            for t in dict_pts[p]:
                conf = {PTN.PID: p}
                val = dict_type[t]
                conf[PTN.TID] = t
                conf[PTN.SC] = dict_pts[p][t]
                conf[PTN.MIV] = val['min_val']
                conf[PTN.MAV] = val['max_val']
                conf[PTN.ITL] = val['interval']
                conf[PTN.DEC] = val['decimal']
                conf[PTN.QA] = dict_tc[t]
                res.append(conf)
    return res


# 获取当前时间，格式：20200929173338500
def get_format_time():
    now = datetime.datetime.now()
    return now.strftime("%Y%m%d%H%M%S%f")[:-3]


# 把占位符替换成时间
def set_time(msg):
    return msg.replace(PTN.TM, get_format_time())


# 获取获取指定范围的一个随机数(字符串类型)，保留三位小数
def random_value(min_val, max_val, decimal):
    return str(round(random.uniform(min_val, max_val), decimal))


# 获取获逗号分隔的字符串，参数是元素为字符串的 tuple
def fmt_msg(*params):
    comma = ','
    res = ()
    for p in params:
        res = res + p
    return comma.join(res)


class MsgUtil:
    """模拟数据消息组装工具类
    消息格式：项目id,17位时间(yyyyMMddHHmmssSSS),功能码,类型id,预留字节,测定个数,指标个数,通讯编号,指标1数值，指标2数值，...
    例子：100,20201015170020100,1,14,1,2,3,1,-7.182,-6.868,2.397,23,-9.077,5.666,-6.547
    参数：conf = {
            'projectId': '27',
            'typeId': '21',
            'serialNumbers': [1, 3, 5],
            'quotaAmount': 5,
            'minValue': 1,
            'maxValue': 10,
            'interval': 100
            'decimal': 100
            }
    """
    # 测点个数，基本信息，测点集合，指标个数，最小值，最大值，小数位数
    tgt_count, base_seq, scs, qa, min_val, max_val, decimal = None, None, None, None, None, None, None
    # 功能码
    fc = '1'
    # 预留字节
    rb = '1'

    def __init__(self, conf):
        try:
            self.qa = conf[PTN.QA]
            self.scs = conf[PTN.SC]
            self.min_val = conf[PTN.MIV]
            self.max_val = conf[PTN.MAV]
            self.decimal = conf[PTN.DEC]
            self.tgt_count = len(self.scs)
            self.base_seq = (
                str(conf[PTN.PID]), PTN.TM, self.fc, str(conf[PTN.TID]), self.rb, str(self.tgt_count), str(self.qa))
        except():
            print("msg util init error, please check the config")

    def msg(self):
        val_seq = ()
        for sc in self.scs:
            val_seq = val_seq + (str(sc),)
            for i in range(1, self.qa + 1):
                ran_val = random_value(self.min_val, self.max_val, self.decimal)
                val_seq = val_seq + (ran_val,)
        return set_time(fmt_msg(self.base_seq, val_seq))


class FakeUtil:
    """数据模拟工具类"""
    channel = None
    exchange = None
    routingKey = None
    conf_list = []

    def __init__(self):
        try:
            # 初始化执行环境
            env = models.FakeEnv.objects.get(is_enable=1)
            self.exchange = env.exchange
            self.routingKey = env.routing_key
            credential = pika.PlainCredentials(env.username, env.password)
            connection = pika.BlockingConnection(
                pika.ConnectionParameters(env.host, env.port, env.vhost, credential, heartbeat=0))
            self.channel = connection.channel()
            # 初始化模拟数据信息
            self.conf_list = parse_config()
        except():
            print("rabbit init error, please check the config")

    @classmethod
    def init(cls):
        try:
            # 初始化执行环境
            obj = models.FakeEnv.objects.filter(is_enable=1).values()
            env = int(cache.get('env'))
            if len(obj) == 0 or obj[0]['env'] != env:
                models.FakeEnv.objects.update(is_enable=0)
                models.FakeEnv.objects.filter(env=env).update(is_enable=1)
            env = models.FakeEnv.objects.get(is_enable=1)
            cls.exchange = env.exchange
            cls.routingKey = env.routing_key
            credential = pika.PlainCredentials(env.username, env.password)
            connection = pika.BlockingConnection(
                pika.ConnectionParameters(env.host, env.port, env.vhost, credential, heartbeat=0))
            cls.channel = connection.channel()
            # 初始化模拟数据信息
            cls.conf_list = parse_config()
        except():
            print("rabbit init error, please check the config")

    @classmethod
    def get_conf_list(cls):
        return cls.conf_list

    def send_to_rabbitmq(self, json):
        self.channel.basic_publish(exchange=self.exchange, routing_key=self.routingKey, body=json)


class RabbitUtil:
    """Rabbitmq工具类"""
    connection = None

    @classmethod
    def init(cls, env):
        try:
            credential = pika.PlainCredentials(env.username, env.password)
            cls.connection = pika.BlockingConnection(
                pika.ConnectionParameters(env.host, env.port, env.vhost, credential, heartbeat=0))
            queue = env.exchange.replace('ex', 'q')
            cls.bind_queue_exchange(queue, env.exchange, env.routing_key)
        except():
            print("rabbit init error, please check the config")

    @classmethod
    def bind_queue_exchange(cls, queue, exchange, routing_key):
        """
        1、首先，如果queue和exchange不存在先创建；
        2、绑定queue和exchange
        """
        channel = cls.connection.channel()
        # 声明queue，如不存在，则创建
        channel.queue_declare(queue=queue, durable=True, arguments={'x-message-ttl': 259200000})
        # 声明exchange，如不存在，则创建
        channel.exchange_declare(exchange=exchange, durable=True, exchange_type='topic')
        # 绑定queue和exchange
        channel.queue_bind(exchange=exchange, queue=queue, routing_key=routing_key)
        cls.connection.close()
